iT邦幫忙

2024 iThome 鐵人賽

DAY 12
0
Software Development

做一支專屬自己學校的課程評價 LINE Bot 吧!系列 第 12

[Day 12] 函式的可重複利用性: 無論輸入課名或老師名都可以跳出選項

  • 分享至 

  • xImage
  •  

函式的可重複利用性

無論是使用者輸入老師或是課名,同樣都是使用 PostbackEvent 觸發事件搭配 FlexSendMessage 訊息回覆,就能跳出課程列表或是開課老師列表,其邏輯也大致相同。

如果要完全再寫一個一樣的函式處理課名,也是完全可行的,但若我們使用了同一份 teacher_lists_flex_message_package 呢?

無論課程列表或是開課老師列表都重複利用同一個函式,這樣程式碼就會變得更簡潔,但如果太簡潔又會看不懂,所以兼具易讀與重複利用高的平衡,是所有軟體工程師的生涯課題,畢竟通常專案都會有一群人一起維護,不像是自己的房間要多亂都沒關西

定義需求

  • 使用者輸入老師名時,會跳出課程列表選項,點選即可跳出對應評價
  • 使用者輸入課程名時,會跳出老師列表選項,點選即可跳出對應評價
  • 選項與回覆皆以 FlexSendMessage 方式回覆

編寫邏輯

  1. 更名檔案使其與涵義一致:因為都是使用同一套模板,先將 reply_course_list.json 重新取名,改為 reply_course_teacher_list.json,養成好習慣,以便日後辨識。

  2. 條件句判斷查詢老師/課名:在監聽使用者傳來的文字訊息時,也要判斷傳來的是課名還是老師名,順手也將函式改為 handle_msg:

    # hulolo > chatbot > views.py
    @parser.add(MessageEvent, message=TextMessage)
    def handle_msg(event):    
        user_message = event.message.text  #取得使用者發送的文字
    
        # 以不同條件做搜尋
        filtered_teacher = Course.objects.filter(teacher_name=user_message)
        filtered_course = Course.objects.filter(course_name=user_message)
    

    透過二分法設計條件句,先添上老師名的判斷:

    # hulolo > chatbot > views.py
    # 接續上段程式碼
        # 判斷老師名的物件是否存在資料
        if filtered_teacher.exists():
                teacher_name = user_message
                candidate_courses = filtered_teacher.values_list(
                    'course_name', flat=True).distinct()
    
                # 修改函式名稱為 dynamic_flex_message_package
                # 多了 label_type 參數讓函式能辨識來源為何
                flex_message = dynamic_flex_message_package(
                    message = FlexSendMessage(
                    alt_text=f"{teacher_name} 老師的課程",
                    contents=flex_message
                    )
                line_bot_api.reply_message(
                    event.reply_token,
                    message
                    )
    
    

    elif 的部分再添上課程名的判斷:

    # hulolo > chatbot > views.py
    # 接續上段程式碼
        # 判斷課程名的物件是否存在資料
        elif filtered_course.exists():
            course_name = user_message
            candidate_teachers = filtered_course.values_list(
            'teacher_name', flat=True).distinct()
    
            # 同樣地,稍後會製作這個漂亮的函式,先呼叫它
            # 多了 label_type 變數讓函式能辨識
            flex_message = dynamic_flex_message_package(
            course_name, candidate_teachers, label_type='teacher')
    
            message = FlexSendMessage(
                                alt_text=f"{course_name} 的老師",
                                contents=flex_message
                            )
            line_bot_api.reply_message(
                        event.reply_token,
                        message)
    
  3. Flex message 的模板處理: dynamic_flex_message_package引入了三個參數,也做了更名,改為較為中性的 title_name 以及 candidate_list,也別忘了 label_type:

    # hulolo > chatbot > views.py
    def dynamic_flex_message_package(title_name, candidate_list, label_type):
        # 引入 JSON 檔案
        json_path = os.path.join(BASE_DIR, 'chatbot', 'reply_course_teacher_list.json')
        flex = json.load(open(json_path, 'r', encoding='utf-8'))    
    
        # 設定標題為傳入的名稱 (可以是老師名或課程名) 這邊使用了單行條件句
        flex['body']['contents'][0]['text'] = f"哪位老師的{title_name}?" if 
            label_type == 'teacher' else f"{title_name}老師的哪堂課?"
    
        # 設定顏色列表
        colors = ['#F0C29E', '#A1DE95', '#F5C578', '#91D9C2', '#DFC493', 
                  '#F0C29E', '#A1DE95', '#F5C578', '#91D9C2', '#DFC493']
    
    
    

    根據傳入的 candidate_list 動態生成按鈕:

        # hulolo > chatbot > views.py
        # 接續上方程式碼
    
        # 根據傳入的 candidate_list 動態生成按鈕
        for i, name in enumerate(candidate_list, start=1):
        button = {
            'type': 'box',
            'layout': 'vertical',
            'spacing': 'none',
            'contents': [{
                'type': 'button',
                'style': 'primary',
                'action': {
                    'type': 'postback',
                    'label': name,
    
                    # 注意這裡的 data 格式,使用了二分法,根據傳入的參數動態生成資料
                    # 因為兩者課名與老師名的順序對調會讓 handle_postback 失效 
                    'data': f"{name}-{title_name}" if 
                        label_type == "teacher" else f"{title_name}-{name}" 
                },
                'color': colors[i-1],
                'margin': 'xs',
                'offsetTop': 'none',
                'height': 'sm'
            }],
            'flex': 0,
            'borderWidth': 'medium',
            'cornerRadius': 'xxl',
            'offsetTop': 'none',
            'margin': 'md',
            'backgroundColor': colors[i-1]
            }
            flex['body']['contents'].append(button)
        return flex
    

    編寫的時候別把課名、老師名相互搞混了~

結果

無論輸入 吳小峰特殊教育學生評量 都能夠跑出對應的列表囉:
https://ithelp.ithome.com.tw/upload/images/20240926/20151510R3hl9NAmMA.png

再往前一點點

  • handle_msg 函式中的條件句其實也有高度類似,可以試著重構讓其更簡潔,但別顧著簡潔失了可讀性。

覆盤

在這篇文章中,我們學會了:

  • 改寫可重複利用函式
  • label_type 參數的代入能夠判斷主程式的類型 (以老師名查詢/以課名查詢)
  • title_namecandidate_list 中性的命名可以容易了解變數的用途
    • 原先為 teacher_namecandidate_courses
  • Day 12 原始碼

上一篇
[Day 11] 以 PostbackEvent 觸發事件,讓使用者輸入老師名就能跑出課程列表
下一篇
[Day 13] 利用資料表做出課程名的簡稱吧!
系列文
做一支專屬自己學校的課程評價 LINE Bot 吧!21
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言